package weifei.live.simulation.im;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import org.wltea.analyzer.IKSegmentation;
import org.wltea.analyzer.Lexeme;
import weifei.live.simulation.Application;
import weifei.live.simulation.TokenVerifier;
import weifei.live.simulation.im.entity.GroupAdminerMessageCache;
import weifei.live.simulation.im.entity.LiveStreamBulletSensitiveWord;
import weifei.live.simulation.entity.User;
import weifei.live.simulation.im.cmd.*;
import weifei.live.simulation.im.exception.OfflineException;
import weifei.live.simulation.im.service.ChatService;
import weifei.live.simulation.im.service.GroupLiveService;

import java.io.IOException;
import java.io.StringReader;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;


@Component
@ServerEndpoint("/im/connect/{token}")
public class IMWebSocket
{
    // 虽然@Component默认是单例模式的，但springboot还是会为每个websocket连接初始化一个bean，所以可以用一个静态set保存起来。
    // 注：底下WebSocket是当前类名
    // 用来存在线连接用户信息
    // 通过userId取出WebSocket
    private static final ConcurrentHashMap<Integer, IMWebSocket> adminerSocketsMap = new ConcurrentHashMap<>();// adminerId, socket
    private static final ConcurrentHashMap<Integer, IMWebSocket> userSocketsMap = new ConcurrentHashMap<>();   // userId，socket
    private static final TokenVerifier tokenVerifier = new TokenVerifier();


    private final ObjectMapper objectMapper = new ObjectMapper();
    private ChatService chatService;
    private GroupLiveService groupLiveService;

    private Session session;
    private String token = null;
    private Integer adminerId = null;
    private Integer userId = null;


    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token)
            throws IOException
    {
        this.chatService = (ChatService) Application.applicationContext.getBean("ChatService");
        this.groupLiveService = (GroupLiveService) Application.applicationContext.getBean("GroupLiveService");


        var user = IMWebSocket.tokenVerifier.verifyUserToken(token);
        if (!IMWebSocket.tokenVerifier.verifyAdminerToken(token) && user == null)
        {
            Message msg = Message.toVisitorTextMessage("无效的token");
            this.sendMessage(Result.message(msg));
            this.session.close();
            return;
        }

        this.session = session;
        this.adminerId = TokenVerifier.getAdminerIDByToken(token);
        this.userId = user.getId();
        this.token = token;


        // 看看连接上的这个用户是否有连接
        IMWebSocket ws_old = null;
        if (this.adminerId != null)
            ws_old = IMWebSocket.adminerSocketsMap.get(this.adminerId);
        else // this.userId != null
            ws_old = IMWebSocket.userSocketsMap.get(this.userId);


        if (ws_old != null) // 有连接
        {
            //窃取 token 来连我，很嚣张
            if (this.token.equals(ws_old.token))
            {
                Message msg = Message.toVisitorTextMessage("系统觉着你可能是窃取 token 来连接，以为你很嚣张");
                this.sendMessage(Result.message(msg));
                this.session.close();
                return;
            }
            // else
            IMWebSocket.userSocketsMap.remove(this.userId);             // 将上一个的 websocket 移除
            ws_old.session.close();                         // 将上一个连接断连
        }


        if (this.adminerId != null)
        {
            IMWebSocket.adminerSocketsMap.put(this.adminerId, this);
            Notice notice = Notice.message("管理员" + this.adminerId + "连接到服务器");
            this.sendMessage(Result.notice(notice));
        }
        else if (this.userId != null)
        {
            IMWebSocket.userSocketsMap.put(this.userId, this);                         // 成功连接上的才会存储
            this.chatService.changeUserState(this.userId, User.State.Online);       // 使用户在线
            Notice notice = Notice.message("用户" + this.userId + "连接到服务器");
            this.sendMessage(Result.notice(notice));
        }

        System.out.println(
                "【websocket消息】有新的连接，总数为:" +
                (
                    IMWebSocket.adminerSocketsMap.size() + IMWebSocket.userSocketsMap.size()
                )
        );


        // 发送消息缓存给用户
        if (this.userId != null)
            IMWebSocket.synchronizeFriendMessageCache(this.userId);

    }


    /**
     * 链接关闭调用的方法
     * 用户 断开连接 会返回一个 两分钟过期 的 token
     */
    @OnClose
    public void onClose()
    {
        System.out.println("【websocket消息】连接断开，总数为:" + userSocketsMap.size());
        if (this.token == null)     // session.close() 会调用这个方法，未成功连上的不用执行下面操作
            return;

        try {
            if (this.adminerId != null)
            {
                adminerSocketsMap.remove(this.adminerId);                   // 成功连接上的才有存储，才需remove
                Notice notice = Notice.message("管理员" + this.adminerId + "断开连接");
                this.sendMessage(Result.notice(notice));
            }
            else if (this.userId != null) {
                this.chatService.changeUserState(this.userId, User.State.Offline);
                userSocketsMap.remove(this.userId);                         // 成功连接上的才有存储，才需remove
                Notice notice = Notice.message("用户" + this.userId + "断开连接");
                this.sendMessage(Result.notice(notice));
            }
        }
        catch (Exception ignored) {}

    }


    /** 发送错误时的处理
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error)
    {
        if (this.adminerId != null)
            System.err.println("管理员" + this.adminerId + " 出现错误，原因:" + error.getMessage());
        else if (this.userId != null)
            System.err.println("用户" + this.userId + " 出现错误，原因:" + error.getMessage());
        Notice notice = Notice.error(error.getMessage());
        this.sendMessage(Result.notice(notice));
        error.printStackTrace();
    }



    /**
     * 收到 客户端 文本消息 后调用的方法
     *
     * @param session
     * @param command
     */
    @OnMessage
    public void onMessageText(Session session, String command)
    {
        try {
            var orders = this.objectMapper.readValue(command, Orders.class);

            switch (orders.getCmd())
            {
                case SessionClose -> {
                    System.out.println("【websocket消息】收到客户端 SessionClose Command:\n" + command);
                    try {
                        this.session.close();
                    }
                    catch (IOException e) { e.printStackTrace(); }
                }

                case SendMessage -> {
                    System.out.println("【websocket消息】收到客户端 SendMessage Command:\n" + command);
                    var node = orders.getData();
                    if (node == null)
                        return;
                    var message = this.objectMapper.readValue(node.toString(), Message.class);
                    this.doSendMessageCommand(message);
                }
                case GetUserMessageCache -> {
                    System.out.println("【websocket消息】收到客户端 GetUserMessageCache Command:" + command);
                    IMWebSocket.synchronizeUserAdminerMessageCache(this.userId);
                    IMWebSocket.synchronizeFriendMessageCache(this.userId);
                }
                case GetGroupMessageCache -> {
                    System.out.println("【websocket消息】收到客户端 GetGroupMessageCache Command:" + command);
                    var node = orders.getData();
                    if (node == null)
                    {
                        IMWebSocket.synchronizeGroupAdminerMessageCache(this.userId, null);
                        return;
                    }
                    var group_id = node.asInt();
                    if (group_id == 0)
                        return;
                    IMWebSocket.synchronizeGroupAdminerMessageCache(this.userId, group_id);
                    IMWebSocket.synchronizeGroupMessageCache(this.userId, group_id);
                }


                case JoinGroupLiveStream -> {
                    System.out.println("【websocket消息】收到客户端 JoinLiveStream Command:" + command);
                    var node = orders.getData();
                    if (node == null)
                        return;
                    var group_id = node.asInt();
                    if (group_id == 0)
                        return;
                    this.doLiveStreamCommand(Orders.Command.JoinGroupLiveStream, group_id);
                }
                case LeaveGroupLiveStream -> {
                    System.out.println("【websocket消息】收到客户端 LeaveLiveStream Command:" + command);
                    this.doLiveStreamCommand(Orders.Command.LeaveGroupLiveStream, null);
                }
                case GetGroupLiveStreamUserIdList -> {
                    System.out.println("【websocket消息】收到客户端 GetLiveStreamUserIdList Command:" + command);

                    var node = orders.getData();
                    if (node == null)
                        return;
                    var group_id = node.asInt();
                    if (group_id == 0)
                        return;
                    this.doLiveStreamCommand(Orders.Command.GetGroupLiveStreamUserIdList, group_id);
                }
                case SendGroupLiveStreamBullet -> {
                    System.out.println("【websocket消息】收到客户端 SendBullet Command:" + command);
                    var node = orders.getData();
                    if (node == null)
                        return;
                    var bullet = this.objectMapper.readValue(node.toString(), Bullet.class);
                    this.doSendGroupLiveStreamBulletCommand(bullet);
                }
                case GetGroupLiveStreamBulletCache -> {
                    System.out.println("【websocket消息】收到客户端 GetGroupLiveStreamBulletCache Command:" + command);
                    var node = orders.getData();
                    if (node == null)
                        return;
                    var group_id = node.asInt();
                    if (group_id == 0)
                        return;
                    IMWebSocket.synchronizeGroupLiveStreamBulletCache(this.userId, group_id);
                }
            }



        }
        catch (JsonProcessingException e) {
            e.printStackTrace();
            System.err.println("【websocket错误】\n命令\n" + command + "\n格式有误");
            Notice notice = Notice.error("Command格式错误");
            this.sendMessage(Result.notice(notice));
        }

    }


    /**
     * 收到 客户端 二进制数据 后调用的方法
     *
     * @param session
     * @param byteBuffer
     */
    @OnMessage
    public void onMessageByteBuffer(Session session, ByteBuffer byteBuffer)
    {
        if (this.adminerId != null)
            return;

        var group_live_stream_manager = this.groupLiveService.getLiveStreamManagerUserInside(this.userId);
        if (group_live_stream_manager == null)  // 用户没加入直播间
            return;

        if (!group_live_stream_manager.isStreamer(this.userId)) // 用户 不是 主播
            return;


        // 查直播间有谁
        // 将语音传给除了 this.userId 的 人

    }




    /**
     * SendMessage Command
     * */
    public void doSendMessageCommand(Message message)
    {
        // 修改 message 的 sendTime
        message.setSendTime(new Date());
        switch (message.getDestination())
        {
            case User -> {
                /* 需要做一个权限检查 */
                if (this.userId != null)
                {
                    System.err.println("用户" + this.userId + "发送 Destination 为 Group 的消息，这个用户可能有问题");
                    var m = Message.toUserTextNotice(
                            new Integer[]{this.userId},
                            "你滴，不能发送 Destination 为 User 的消息"
                    );
                    this.sendMessage(Result.message(m));
                    return;
                }

                this.sendMessageToUsersByAdminer(message);
            }
            case Group -> {
                /* 需要做一个权限检查 */
                if (this.userId != null)
                {
                    System.err.println("用户" + this.userId + "发送 Destination 为 Group 的消息，这个用户可能有问题");
                    var m = Message.toUserTextNotice(
                            new Integer[]{this.userId},
                            "你滴，不能发送 Destination 为 Group 的消息"
                    );
                    this.sendMessage(Result.message(m));
                    return;
                }

                this.sendMessageToGroupByAdminer(message);     // 消息缓存放在了里面
            }
            case UserToFriend -> {
                if (this.adminerId != null)
                {
                    System.err.println("管理员" + this.adminerId + "发送 Destination 为 UserToFriend 的消息");
                    var m = Message.toAdminerTextNotice(
                            new Integer[]{this.adminerId},
                            "管理员" + this.adminerId + "请不要调皮，假装他人发消息"
                    );
                    this.sendMessage(Result.message(m));
                    return;
                }
                this.sendMessageToFriendsByUser(message);   // 消息缓存放在了里面
            }
            case UserToGroup -> {
                if (this.adminerId != null)
                {
                    System.err.println("管理员" + this.adminerId + "发送 Destination 为 UserToGroup 的消息");
                    var m = Message.toAdminerTextNotice(
                            new Integer[]{this.adminerId},
                            "管理员" + this.adminerId + "请不要调皮，假装他人发消息"
                    );
                    this.sendMessage(Result.message(m));
                    return;
                }
                this.sendMessageToGroupsByUser(message);    // 消息缓存放在了里面
            }
        }
    }


    /**
     * 直播间Command 处理
     * */
    public void doLiveStreamCommand(Orders.Command cmd, Integer groupId)
    {
        if (this.userId == null)
            return;
        if (cmd != Orders.Command.LeaveGroupLiveStream && groupId == null)
            return;

        GroupLiveStreamManager manager = null;
        if (cmd != Orders.Command.LeaveGroupLiveStream)
            manager = this.groupLiveService.getLiveStreamManagerByGroupID(groupId);
        else
        {
            manager = this.groupLiveService.getLiveStreamManagerUserInside(this.userId);
            if (manager == null)    // 未加入直播间，离开什么
                return;
        }

        if (manager == null)
        {
            System.err.println("用户" + this.userId + "加入不存在的群聊直播间，这个用户可能有问题");
            var m = Message.toUserTextNotice(
                    new Integer[]{this.userId},
                    "不存在的直播间"
            );
            this.sendMessage(Result.message(m));
            return;
        }

        switch (cmd)
        {
            case JoinGroupLiveStream -> {
                manager.addUserId(this.userId);
            }
            case LeaveGroupLiveStream -> {
                manager.removeUserId(this.userId);
            }
            case GetGroupLiveStreamUserIdList -> {
                var id_array = manager.getUserIds();
                this.sendMessage(Result.data(id_array));
            }
        }


    }



    /**
     * 发送弹幕命令
     * */
    public void doSendGroupLiveStreamBulletCommand(Bullet bullet)
    {
        // 敏感字词过滤
        IKSegmentation ikSeg = new IKSegmentation(new StringReader(bullet.getContent()));
        try {
            Lexeme l = null;
            var qw_bsw = new QueryWrapper<LiveStreamBulletSensitiveWord>();
            var bsw = new LiveStreamBulletSensitiveWord();
            while( (l = ikSeg.next()) != null)
            {
                System.out.println(l);
                var word = l.getLexemeText();
                qw_bsw.clear();
                qw_bsw.eq(LiveStreamBulletSensitiveWord.Column.Word.getName(), word);
                var sensitive_word = bsw.selectOne(qw_bsw);
                if (sensitive_word != null) {
                    var new_word = new char[word.length()];
                    Arrays.fill(new_word, '*');
                    var r = bullet.getContent().replace(word, new String(new_word));
                    bullet.setContent(r);
                }
            }
        } catch (IOException e) {
            var m = Message.toUserTextNotice(new Integer[]{this.userId}, "弹幕发送失败");
            this.sendMessage(Result.message(m));
            return;
        }


        // 修改弹幕发送时间
        bullet.setSendTime(new Date());

        // 判断 senderID
        if (!bullet.getSenderId().equals(this.userId))
        {
            System.err.println("用户" + this.userId + "发送了 senderID 不为自己的消息");
            var m = Message.toUserTextNotice(new Integer[]{this.userId}, "弹幕发送失败");
            var n = Notice.userException("用户" + this.userId + "发送了 senderID 不为自己的弹幕");
            this.sendMessage(Result.message(m));
            this.sendMessage(Result.notice(n));
            return;
        }
        // 判断 直播间 是否开启
        var group_id = bullet.getTargetId();
        var live_stream_manager = this.groupLiveService.getLiveStreamManagerByGroupID(group_id);
        if (live_stream_manager == null)
        {
            System.err.println("用户" + this.userId + "向未开启的 群聊直播间 发送弹幕");
            var m = Message.toUserTextNotice(new Integer[]{this.userId}, "直播间未开启");
            var n = Notice.userException("用户" + this.userId + "向未开启的 群聊直播间 发送弹幕");
            this.sendMessage(Result.message(m));
            this.sendMessage(Result.notice(n));
            return;
        }
        this.groupLiveService.cacheBullet(bullet);

        var live_manager = this.groupLiveService.getLiveStreamManagerByGroupID(group_id);
        var user_ids = live_manager.getUserIds();
        for (var i : user_ids)
        {
            try {
                var ws = IMWebSocket.getUserSocket(i);
                ws.sendMessage(Result.groupLiveStreamBullet(bullet));
            }
            catch (OfflineException e) {  return; }
        }
    }


    /**
     * 朋友消息同步
     * */
    @JmsListener(destination = "im.message.friend.cache1")
    public static void synchronizeFriendMessageCache(Integer userId)
    {
        if (userId == null)
            return;

        IMWebSocket ws = null;
        try {
            ws = IMWebSocket.getUserSocket(userId);
        } catch (OfflineException e) {
            return;
        }
        ChatService chat_service = (ChatService) Application.applicationContext.getBean("ChatService");

        var list = chat_service.getFriendMessageCacheListByUserID(userId);
        if (list == null)
            return;
        for(var cache : list)
        {
            var m = cache.toMessage();
            ws.sendMessage(Result.message(m));
        }
    }


    /**
     * 群聊消息同步
     * */
    public static void synchronizeGroupMessageCache(int userId, int groupId)
    {
        IMWebSocket ws = null;
        try {
            ws = IMWebSocket.getUserSocket(userId);
        }
        catch (OfflineException e) {return;}
        ChatService chat_service = (ChatService) Application.applicationContext.getBean("ChatService");

        var list = chat_service.getGroupMessageCacheListByGroupID(groupId);
        if (list == null)
            return;
        for(var cache : list)
        {
            var m = cache.toMessage();
            ws.sendMessage(Result.message(m));
        }
    }


    /**
     * 用户广播消息同步
     * */
    @JmsListener(destination = "im.message.adminer.user.cache1")
    public static void synchronizeUserAdminerMessageCache(Integer userId)
    {
        if (userId == null)
            return;

        IMWebSocket ws = null;
        try {
            ws = IMWebSocket.getUserSocket(userId);
        } catch (OfflineException e) {
            return;
        }
        ChatService chat_service = (ChatService) Application.applicationContext.getBean("ChatService");

        var list = chat_service.getAllUserAdminerMessageCacheList();
        if (list == null)
            return;
        for(var cache : list)
        {
            var m = cache.toMessage();
            ws.sendMessage(Result.message(m));
        }

        list = chat_service.getUserAdminerMessageCacheListByUserID(userId);
        if (list == null)
            return;
        for(var cache : list)
        {
            var m = cache.toMessage();
            ws.sendMessage(Result.message(m));
        }
    }


    /**
     * 群广播消息同步
     * */
    public static void synchronizeGroupAdminerMessageCache(int userId, Integer groupId)
    {
        IMWebSocket ws = null;
        try {
            ws = IMWebSocket.getUserSocket(userId);
        }
        catch (OfflineException e) {return;}
        ChatService chat_service = (ChatService) Application.applicationContext.getBean("ChatService");

        List<GroupAdminerMessageCache> list = null;
        if (groupId == null)
            list = chat_service.getAllGroupAdminerMessageCacheList();
        else
            list = chat_service.getGroupAdminerMessageCacheListByGroupID(groupId);

        if (list == null)
            return;
        for(var cache : list)
        {
            var m = cache.toMessage();
            ws.sendMessage(Result.message(m));
        }
    }


    /**
     * 群聊直播间 弹幕缓存 同步
     * */
    public static void synchronizeGroupLiveStreamBulletCache(int userId, int groupId)
    {
        IMWebSocket ws = null;
        try {
            ws = IMWebSocket.getUserSocket(userId);
        }
        catch (OfflineException e) {return;}

        GroupLiveService live_service = (GroupLiveService) Application.applicationContext.getBean("GroupLiveService");
        var list = live_service.getBulletCacheList(groupId);
        for (var cache : list)
            ws.sendMessage(Result.groupLiveStreamBullet(cache.toBullet()));
    }


    public static IMWebSocket getAdminerSocket(int adminerId)
            throws OfflineException
    {
        var ws = IMWebSocket.adminerSocketsMap.get(adminerId);
        if (ws == null)
            throw new OfflineException("管理员" + adminerId + " 未上线");
        return ws;
    }


    public static IMWebSocket getUserSocket(int userId)
            throws OfflineException
    {
        var ws = IMWebSocket.userSocketsMap.get(userId);
        if (ws == null)
            throw new OfflineException("用户" + userId + " 未上线");           // 朋友消息做缓存
        return ws;
    }


    /**
     * 发送 文本消息 给 客户端
     * */
    public void sendMessage(Result result)
    {
        if (this.session != null && this.session.isOpen())
        {
            var tree = this.objectMapper.valueToTree(result);
            this.session.getAsyncRemote().sendText(tree.toString());
        }
    }



    /**
     * 管理员 发送消息给 用户
     * */
    public void sendMessageToUsersByAdminer(Message message)
    {
        if (this.adminerId == null)
        {
            System.err.println("你调用错了——来自sendMessageToUsersByAdminer");
            return;
        }
        if (!message.getSenderId().equals(this.adminerId))
        {
            System.err.println("管理员" + this.adminerId + "发送了 senderID 不为自己的消息");
            var m = Message.toAdminerTextNotice(new Integer[]{this.adminerId}, "senderID错误");
            this.sendMessage(Result.message(m));
            return;
        }


        var targetIds = message.getTargetIds();
        if (targetIds == null)
        {
            System.out.println("【websocket消息】广播用户消息:" + message);
            this.chatService.cacheUserAdminerMessage(null, message);
            for (var ws : IMWebSocket.userSocketsMap.values())                // 将消息发给所有在线用户
                ws.sendMessage(Result.message(message));
            return;
        }


        var m = new Message(message);
        m.setTargetIds(new Integer[1]);
        for (var i : targetIds)
        {
            if (!this.chatService.hasUser(i))
            {
                System.out.println("管理员" + this.adminerId + "怎么搞的！把消息发送给不存在的用户");
                var mn = Message.toAdminerTextNotice(new Integer[]{this.adminerId}, "将消息发送给了不存在的用户");
                this.sendMessage(Result.message(mn));
                continue;
            }

            m.getTargetIds()[0] = i;    // 修改 发送的消息的 targetID
            try {
                var ws_target = IMWebSocket.getUserSocket(i); // 发送给用户
                ws_target.sendMessage(Result.message(m));
                this.sendMessage(Result.message(m));     // 将 消息 转发给 sender
            }
            catch (OfflineException e) {
                this.chatService.cacheUserAdminerMessage(i, message);
            }
        }

    }


    public void sendMessageToGroupByAdminer(Message message)
    {
        if (this.adminerId == null)
        {
            System.err.println("adminerId为null你调用错了——sendMessageToGroupByAdminer");
            return;
        }
        if (!message.getSenderId().equals(this.adminerId))
        {
            System.err.println("管理员" + this.adminerId + "发送了 senderID 不为自己的消息");
            var m = Message.toAdminerTextNotice(new Integer[]{this.adminerId}, "senderID错误");
            this.sendMessage(Result.message(m));
            return;
        }
        this.chatService.cacheGroupAdminerMessage(message);


        var target_ids = message.getTargetIds();
        if (target_ids == null)
        {
            System.out.println("【websocket消息】广播群聊消息:" + message);
            for (var ws : IMWebSocket.userSocketsMap.values())                // 将消息发给所有在线用户
                ws.sendMessage(Result.message(message));
            return;
        }


        var m = new Message(message);
        m.setTargetIds(new Integer[1]);
        for (var i : target_ids)
        {
            if (!this.chatService.hasGroup(i))
            {
                System.out.println("管理员" + this.adminerId + "怎么搞的！把消息发送给不存在的群聊");
                var mn = Message.toAdminerTextNotice(new Integer[]{this.adminerId}, "将消息发送给了不存在的群聊");
                this.sendMessage(Result.message(mn));
                continue;
            }

            m.getTargetIds()[0] = i;    // 修改 发送的消息的 targetID
            var userIds = this.chatService.getGroupUserIds(i);
            for (var uid : userIds)
            {
                try {
                    var ws_target = IMWebSocket.getUserSocket(uid); // 发送给用户
                    ws_target.sendMessage(Result.message(m));
                }
                catch (OfflineException ignored) {}
            }

            // 将 消息 转发给 sender
            this.sendMessage(Result.message(m));
        }

    }


    /**
     * 用户 发送消息给 朋友
     * */
    public void sendMessageToFriendsByUser(Message message)
    {
        var sender_id = message.getSenderId();
        var target_ids = message.getTargetIds();
        if (!sender_id.equals(this.userId))
        {
            System.err.println("用户" + this.userId + "发送了senderID不为自己的消息，这个用户可能有问题");
            var m = Message.toUserTextNotice(new Integer[]{this.userId}, "消息发送失败");
            var n = Notice.userException("senderID错误");
            this.sendMessage(Result.message(m));
            this.sendMessage(Result.notice(n));
            return;
        }
        if (target_ids == null)
        {
            System.err.println("用户" + sender_id + "发送了格式错误的消息，这个用户可能有问题");
            var m = Message.toUserTextNotice(new Integer[]{this.userId}, "缺少发送对象");
            var n = Notice.userException("UserToFriend targetIds 为 null");
            this.sendMessage(Result.message(m));
            this.sendMessage(Result.notice(n));
            return;
        }


        var m = new Message(message);
        m.setTargetIds(new Integer[1]);
        for (var i : target_ids)
        {
            if (!this.chatService.hasUser(i))
            {
                System.out.println("有人乱搞！把消息发送给不存在的用户");
                var mn = Message.toUserTextNotice(new Integer[]{this.userId}, "将消息发送给了不存在的用户");
                var n = Notice.userException("用户" + this.userId + " 将消息发送给了不存在的用户");
                this.sendMessage(Result.message(mn));
                this.sendMessage(Result.notice(n));
                continue;
            }

            //判断对方与自己是否互为好友
            if (!this.chatService.bothFriend(this.userId, i))
            {
                var mn = Message.toUserTextNotice(
                    new Integer[]{this.userId},
                    "你不是用户" + i + "的朋友；或者，用户" + i + "不是你的朋友"
                );
                this.sendMessage(Result.message(mn));
                continue;   //下一个
            }


            m.getTargetIds()[0] = i;    // 修改 发送的消息的 targetID
            try {
                var ws_target = IMWebSocket.getUserSocket(i); // 发送给用户
                ws_target.sendMessage(Result.message(m));
                this.sendMessage(Result.message(m));     // 将 消息 转发给 sender
            }
            catch (OfflineException e) {
                this.chatService.cacheUserFriendMessage(i, message);
            }
        }

    }


    /**
     * 用户 发送消息到 群聊
     * */
    public void sendMessageToGroupsByUser(Message message)
    {
        var sender_id = message.getSenderId();
        var target_ids = message.getTargetIds();
        if (!sender_id.equals(this.userId))
        {
            System.err.println("用户" + this.userId + "发送了senderID不为自己的消息，这个用户可能有问题");
            var m = Message.toUserTextNotice(new Integer[]{this.userId}, "senderID错误");
            var n = Notice.userException("senderID错误");
            this.sendMessage(Result.message(m));
            this.sendMessage(Result.notice(n));
            return;
        }
        // 消息格式有误
        if (target_ids == null)
        {
            System.err.println("用户" + sender_id + "发送了格式错误的消息，这个用户可能有问题");
            var m = Message.toUserTextNotice(new Integer[]{this.userId}, "缺少发送对象");
            var n = Notice.userException("UserToGroup targetIds 为 null");
            this.sendMessage(Result.message(m));
            this.sendMessage(Result.notice(n));
            return;
        }
        // 消息缓存
        this.chatService.cacheGroupUserMessage(message);


        var m = new Message(message);
        m.setTargetIds(new Integer[1]);
        for (var i : target_ids)
        {
            if (!this.chatService.hasGroup(i))
            {
                System.out.println("用户" + this.userId + "怎么搞的！把消息发送给不存在的群聊");
                var mn = Message.toUserTextNotice(new Integer[]{this.userId}, "将消息发送给了不存在的群聊");
                var n = Notice.userException("用户" + this.userId + " 将消息发送给了不存在的群聊");
                this.sendMessage(Result.message(mn));
                this.sendMessage(Result.notice(n));
                continue;
            }

            if (!this.chatService.hasJoinedGroup(this.userId, i))
            {
                var mn = Message.toUserTextNotice(new Integer[]{this.userId}, "你还没有加入群聊" + i);
                this.sendMessage(Result.message(mn));
                continue;
            }

            m.getTargetIds()[0] = i;    // 修改 发送的消息的 targetID
            var userIds = this.chatService.getGroupUserIds(i);
            for (var uid : userIds)
            {
                try {
                    var ws = IMWebSocket.getUserSocket(uid);
                    ws.sendMessage(Result.message(m));
                }
                catch (OfflineException ignored) {}
            }
            // 将 消息 转发给 sender
            this.sendMessage(Result.message(m));
        }

    }




}


